﻿#pragma once

#include <qobject.h>
#include <qvector.h>
#include <qtimer.h>

class TurnTableCodec;
class ScriptInstruction;
class LogSaver;

class ScriptRunner : public QObject
{
  Q_OBJECT

public:
  //代码段参数
private:
  //第一层下标代表代码段索引，第二层下标代表对应代码段中的每个脚本指针的索引，值是脚本指针。
  QVector<QVector<ScriptInstruction*>> script;

  //出现循环的情况下的现场保护
  //代码块栈，栈顶值代表当前运行的代码块的索引
  QVector<int> scriptIndexStack;
  //脚本指令栈，栈顶值代表在当前代码块中的下一条指令的索引
  QVector<int> nextInstructionIndexStack;
  //当前执行的指令在当前代码块中的索引
  int currentInstructionIndex = 0;
  //循环计数栈，栈顶代表当前代码段的剩余循环次数
  QVector<int> loopCountStack;

  //脚本状态
  bool state_script = false;//用于脚本运行控制

  int interval_instruction = 200;//指令间正常时间间隔
  int interval_connectionTimeOut = 1000;//设置连接超时触发时间，与转台连接状态管理器冲突，
  int interval_turnTableOperationTimeOut = 30000;//转台操作超时上限

  QTimer timer_wait;

  TurnTableCodec* turnTableCodec = Q_NULLPTR;
  LogSaver* logSaver = Q_NULLPTR;
  //友元指令类------------------------------------------------
  friend class InstructionDataReaderStartCollection;
  friend class InstructionDataReaderStopCollection;
  friend class InstructionLoop;
  friend class InstructionThermostatConnect;
  friend class InstructionThermostatDisconnect;
  friend class InstructionThermostatTargetTemperature;
  friend class InstructionThermostatTemperatureRate;
  friend class InstructionTurnTableCloseCircuit;
  friend class InstructionTurnTableConnect;
  friend class InstructionTurnTableDisconnect;
  friend class InstructionTurnTableEmergencyStop;
  friend class InstructionTurnTableOpenCircuit;
  friend class InstructionTurnTablePositionMode;
  friend class InstructionTurnTablePowerOff;
  friend class InstructionTurnTablePowerOn;
  friend class InstructionTurnTableQuitConsole;
  friend class InstructionTurnTableRateMode;
  friend class InstructionTurnTableRun;
  friend class InstructionTurnTableSetLoad;
  friend class InstructionTurnTableStop;
  friend class InstructionWait;
  //--------------------------------------------------------------
public:
  ScriptRunner(QObject* parent = Q_NULLPTR);
  ~ScriptRunner();

signals:
  //当脚本开始时会触发该信号
  void signal_scriptStarted();
  //当脚本停止时会触发该信号
  void signal_scriptStoped();
  void signal_emptyScript();
  //当当前运行指令改变时会触发该信号，参数int codeBlockIndex代表当前所在代码块索引，参数int instructionIndex代表当前指令在当前代码块中的索引
  void signal_currentInstruction(int codeBlockIndex, int instructionIndex);
  //当脚本改变时会触发该信号，参数QVector<QVector<ScriptInstruction*>> scriptList是当前脚本
  void signal_scriptListChanged(QVector<QVector<ScriptInstruction*>> scriptList);

public slots:
  //启动脚本，脚本将按照预定计划开始执行
  void slot_startScript();
  //正常停止脚本，只停止下一步行动，重置各状态，不作出任何其余的处理，正常退出或者不需要继续脚本操作时使用
  void slot_stopScript();
  //紧急停止脚本，立刻停止下一步行动，并发送停止执行给转台，紧急情况下使用
  void slot_emergencyStopScript();
public:
  bool getScriptState();
private:
  void scriptWaitAndRun(int millisecond);//等待millisecond毫秒后自动执行下个指令

private slots:
  void slot_turnTableError();//转台出错时停止脚本
  void slot_timer_wait_timeOut();

public slots:
  void slot_stateCheckerTimeOut();
  void slot_conditionReached();
  //代码优化，模块化
private:
  //运行下一条指令
  void runNextInstruction();
  //重置脚本到初始状态
  void initScript();
  //开启一个新的循环，目标代码块和循环次数入栈
  void newLoop(int scriptIndex, int loopCount);
  //循环层销毁，执行完毕的代码块出栈
  void loopFinished();
  //检查是否存在下一条可运行的指令
  bool ifCanRun();

  //geter and seter
public:
  QVector<QVector<ScriptInstruction*>> getScript();
  void setScript(QVector<QVector<ScriptInstruction*>> script);
  int getInterval_instruction();
  void setInterval_instruction(int interval_instruction);
  int getTime_connectTimeOut();
  void setTime_connectTimeOut(int interval_connectionTimeOut);
  int getInterval_turnTableOperationTimeOut();
  void setInterval_turnTableOperationTimeOut(int interval_turnTableOperationTimeOut);
  TurnTableCodec* getTurnTableCodec();
  void setTurnTableCodec(TurnTableCodec* turnTableCodec);
  LogSaver* getLogSaver();
  void setLogSaver(LogSaver* logSaver);
};
